﻿#include "FITKSetSurfPolyProvider.h"

// VTK
#include <vtkSmartPointer.h>
#include <vtkDataSet.h>
#include <vtkUnstructuredGrid.h>
#include <vtkUnstructuredGrid.h>
#include <vtkUnstructuredGrid.h>
#include <vtkSelectionNode.h>
#include <vtkSelection.h>
#include <vtkExtractSelection.h>
#include <vtkIdTypeArray.h>
#include <vtkAppendFilter.h>
#include <vtkTransform.h>
#include <vtkTransformFilter.h>
#include <vtkExtractSelection.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkDoubleArray.h>
#include <vtkPlane.h>
#include <vtkPolygon.h>

// Global data
#include "FITK_Kernel/FITKCore/FITKDataRepo.h"

// Data
#include "FITK_Interface/FITKInterfaceModel/FITKMeshSurfaceElement.h"
#include "FITK_Interface/FITKInterfaceModel/FITKMeshSurfaceNode.h"

#include "FITK_Component/FITKAbaqusData/FITKAbaqusPart.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAssInstance.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaMeshPartVTK.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaAssembly.h"

#include "FITK_Interface/FITKInterfaceStructural/FITKAbaSurfaceLabel.h"
#include "FITK_Interface/FITKInterfaceModel/FITKModelSet.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAbstractElement.h"
#include "FITK_Component/FITKAbaqusData/FITKAbaModelInfo.h"
#include "FITK_Kernel/FITKCore/FITKVec3D.h"

// Graph
#include "FITKGraphCommons.h"

namespace Exchange
{
    // Initialize the static instance.
    FITKSetSurfPolyProvider* FITKSetSurfPolyProvider::s_instance{ nullptr };

    FITKSetSurfPolyProvider* FITKSetSurfPolyProvider::getInstance()
    {
        if (!s_instance)
        {
            s_instance = new FITKSetSurfPolyProvider;
        }

        return s_instance;
    }

    FITKSetSurfPolyProvider::FITKSetSurfPolyProvider()
    {

    }

    FITKSetSurfPolyProvider::~FITKSetSurfPolyProvider()
    {
        // Delete set poly data.
        for (QHash<int, vtkUnstructuredGrid*> & hash : m_setGridHash.values())
        {
            for (vtkUnstructuredGrid* grid : hash.values())
            {
                if (grid)
                {
                    grid->Delete();
                }
            }

            hash.clear();
        }

        m_setGridHash.clear();

        // Delete surface poly data.
        for (QHash<int, vtkUnstructuredGrid*> & hash : m_surfaceGridHash.values())
        {
            for (vtkUnstructuredGrid* grid : hash.values())
            {
                if (grid)
                {
                    grid->Delete();
                }
            }

            hash.clear();
        }

        m_surfaceGridHash.clear();
    }

    bool FITKSetSurfPolyProvider::IsShellSurface(int surfaceId, int instanceId)
    {
        // Try to get the surface data.
        Interface::FITKMeshSurfaceElement* surface = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKMeshSurfaceElement>(surfaceId);
        if (!surface)
        {
            return false;
        }

        // Check if the surface is valid.
        if (surface->getMeshSurfaceType() != Interface::FITKMeshSurface::MeshSurfaceType::SurEle)
        {
            return false;
        }

        // Get all valid sets.
        QList<Interface::SurfaceAbsSetInfo> surfSetInfoList = surface->getAbsoluteSetInfo();

        // Get the number of sets.
        int nSet = surfSetInfoList.count();

        // Check all set's model, if there is an shell instance or part.
        for (int i = 0; i < nSet; i++)
        {
            Interface::SurfaceAbsSetInfo surfSetInfo = surfSetInfoList[i];

            // Get the set data.
            Interface::FITKModelSet* set = surfSetInfo._set;
            if (!set)
            {
                continue;
            }

            // Get the set's parent data.
            Interface::FITKAbstractModel* parent = surfSetInfo._model;

            // Get set's model information.
            AbaqusData::AbaModelInfo setInfo = AbaqusData::FITKAbaModelInfo::GetModelInfo(set->getDataObjectID(), instanceId);

            AbaqusData::FITKAbaqusPart* part{ nullptr };
            Interface::FITKAbstractAssInstance* instance{ nullptr };

            bool isValid = false;

            // Try to get the model data as an instance.
            if (setInfo._instance)
            {
                isValid = true;
                instance = setInfo._instance;
                part = setInfo._instance->getModelT<AbaqusData::FITKAbaqusPart>();
            }

            // Try to cast the parent data to instance.
            Interface::FITKAbstractAssInstance* instanceParent = dynamic_cast<Interface::FITKAbstractAssInstance*>(parent);
            if (!isValid && instanceParent)
            {
                isValid = true;
                instance = instanceParent;
                part = instanceParent->getModelT<AbaqusData::FITKAbaqusPart>();
            }

            // Try to cast the parent data to part.
            AbaqusData::FITKAbaqusPart* partParent = Core::FITKDataRepo::getInstance()->getTDataByID<AbaqusData::FITKAbaqusPart>(surface->getAbsModelID());
            if (!isValid && partParent)
            {
                isValid = true;
                part = partParent;
            }

            // Try to check the instance.
            if (!isValid && setInfo._instance)
            {
                isValid = true;
                instance = setInfo._instance;
                part = setInfo._instance->getModelT<AbaqusData::FITKAbaqusPart>();
            }

            // Try to check to the part.
            if (!isValid && setInfo._part)
            {
                isValid = true;
                part = setInfo._part;
            }

            // Check the part.
            if (!part)
            {
                return false;
            }

            // Get model mesh data.
            Interface::FITKAbaMeshPartVTK* meshData = part->getMesh();
            if (!meshData)
            {
                return false;
            }

            Interface::FITKModelEnum::FITKMeshDim dim = meshData->getMeshDim();
            if (dim == Interface::FITKModelEnum::FITKMeshDim::FMDimD2)
            {
                return true;
            }
        }

        return false;
    }

    bool FITKSetSurfPolyProvider::IsShellSurfaceNoneEdge(int surfaceId, int instanceId)
    {
        bool isShell = IsShellSurface(surfaceId, instanceId);
        if (!isShell)
        {
            return false;
        }

        // Check the surface label.
        Interface::FITKMeshSurfaceElement* surface = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKMeshSurfaceElement>(surfaceId);

        // Get all valid sets' info.
        QList<Interface::SurfaceAbsSetInfo> surfSetInfoList = surface->getAbsoluteSetInfo();
        for (const Interface::SurfaceAbsSetInfo & info : surfSetInfoList)
        {
            if (info._surfaceIndex != Interface::FITKAbaSurfaceLabel::AbaSurfLabel::SPOS &&
                info._surfaceIndex != Interface::FITKAbaSurfaceLabel::AbaSurfLabel::SNEG)
            {
                return false;
            }
        }

        return true;
    }

    QList<int> FITKSetSurfPolyProvider::GetCompSetSubSetIds(int setId)
    {
        QList<int> subIds;

        // Check the set data.
        Interface::FITKModelSet* set = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKModelSet>(setId);
        if (!set)
        {
            return subIds;
        }

        // Check if the set is a composite set.
      //  if (set->getModelSetType() != Interface::FITKModelEnum::FITKModelSetType::FMSComb)
        if (set->getModelSetType() != Interface::FITKModelEnum::FITKModelSetType::FMSMIX)
        {
            return subIds;
        }

        int nSubSet = set->getDataCount();
        for (int i = 0; i < nSubSet; i++)
        {
            subIds.push_back(set->getDataByIndex(i)->getDataObjectID());
        }

        return subIds;
    }

    QList<int> FITKSetSurfPolyProvider::GetSurfaceSubSetIds(int surfaceId)
    {
        QList<int> subIds;

        // Check the surface data.
        Interface::FITKMeshSurface* surface = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKMeshSurface>(surfaceId);
        if (!surface)
        {
            return subIds;
        }

        // Get all valid sets.
        QList<Interface::SurfaceAbsSetInfo> surfSetInfoList = surface->getAbsoluteSetInfo();

        // Get the number of sets.
        int nSet = surfSetInfoList.count();
        
        for (int i = 0; i < nSet; i++)
        {
            Interface::FITKModelSet* set = surfSetInfoList[i]._set;
            if (set)
            {
                subIds.push_back(set->getDataObjectID());
            }
        }

        return subIds;
    }

    const char* FITKSetSurfPolyProvider::getModelIdsArrayName()
    {
        // Return the const name of the array.
        return c_modelIdArrayName;
    }

    vtkUnstructuredGrid* FITKSetSurfPolyProvider::getSetGridData(int setId, int& modelId, int instanceId)
    {
        // Get the set data.
        Interface::FITKModelSet* set = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKModelSet>(setId);
        if (!set)
        {
            return nullptr;
        }

        // Try to get the data id.
        int keyId = instanceId;
        vtkUnstructuredGrid* setGrid{ nullptr };
        if (keyId == -1)
        {
            // Get the part or assembly's set by parent id.
            keyId = set->getAbsModelID();
        }

        // Try to get the set data.
        if (m_setGridHash.contains(setId))
        {
            setGrid = m_setGridHash[setId][instanceId];
            modelId = m_setGridModelIdsHash[setId][instanceId];
        }

        if (setGrid)
        {
            return setGrid;
        }

        // Generate the set grid.
        setGrid = generateSetGrid(set, modelId, instanceId);
        if (!setGrid)
        {
            return nullptr;
        }

        // Save the mesh data and model id.
        m_setGridHash[setId][keyId] = setGrid;
        m_setGridModelIdsHash[setId][keyId] = modelId;  

        // Check if the data object is destroyed.
        //@{
        connect(set, &Interface::FITKModelSet::dataObjectDestoried, this, [=]
        {
            QHash<int, vtkUnstructuredGrid*> & setHash = m_setGridHash[setId];
            for (vtkUnstructuredGrid* grid : setHash.values())
            {
                if (grid)
                {
                    grid->Delete();
                }
            }

            setHash.clear();
            m_setGridHash.take(setId);

            m_setGridModelIdsHash[setId].clear();
            m_setGridModelIdsHash.take(setId);

        }, Qt::UniqueConnection);
        //@}

        // Add the model ids array to point data and cell data.
        vtkSmartPointer<vtkIntArray> modelIdArrayPt = vtkSmartPointer<vtkIntArray>::New();
        modelIdArrayPt->SetName(c_modelIdArrayName);
        setGrid->GetPointData()->AddArray(modelIdArrayPt);

        vtkSmartPointer<vtkIntArray> modelIdArrayCell = vtkSmartPointer<vtkIntArray>::New();
        modelIdArrayCell->SetName(c_modelIdArrayName);
        setGrid->GetCellData()->AddArray(modelIdArrayCell);

        // Fill the model id array to point data.
        int nPt = setGrid->GetNumberOfPoints();
        modelIdArrayPt->SetNumberOfComponents(1);
        modelIdArrayPt->SetNumberOfValues(nPt);
        modelIdArrayPt->FillComponent(0, modelId);

        // Fill the model id array to point data.
        int nCell = setGrid->GetNumberOfCells();
        modelIdArrayCell->SetNumberOfComponents(1);
        modelIdArrayCell->SetNumberOfValues(nCell);
        modelIdArrayCell->FillComponent(0, modelId);

        return setGrid;
    }

    vtkUnstructuredGrid* FITKSetSurfPolyProvider::getSurfaceGridData(int surfaceId, QList<int>& modelIds, int instanceId)
    {
        modelIds.clear();

        // Get the surface data.
        Interface::FITKMeshSurface* surface = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKMeshSurface>(surfaceId);
        if (!surface)
        {
            return nullptr;
        }

        // Try to get the data id.
        int keyId = instanceId;
        vtkUnstructuredGrid* surfaceGrid{ nullptr };
        if (keyId == -1)
        {
            // Get the part or assembly's surface by parent id.
            keyId = surface->getAbsModelID();
        }

        // Try to get the surface data.
        if (m_surfaceGridHash.contains(surfaceId))
        {
            surfaceGrid = m_surfaceGridHash[surfaceId][keyId];
            modelIds = m_surfaceGridModelIdsHash[surfaceId][keyId];
        }

        if (surfaceGrid)
        {
            return surfaceGrid;
        }

        // Generate the surface grid.
        surfaceGrid = generateSurfaceGrid(surface, modelIds, instanceId);
        if (!surfaceGrid)
        {
            return nullptr;
        }

        // Save the mesh data and model ids.
        m_surfaceGridHash[surfaceId][keyId] = surfaceGrid;
        m_surfaceGridModelIdsHash[surfaceId][keyId] = modelIds;

        // Check if the data object is destroyed.
        //@{
        connect(surface, &Interface::FITKMeshSurface::dataObjectDestoried, this, [=]
        {
            QHash<int, vtkUnstructuredGrid*> & surfaceHash = m_surfaceGridHash[surfaceId];
            for (vtkUnstructuredGrid* grid : surfaceHash.values())
            {
                if (grid)
                {
                    grid->Delete();
                }
            }

            surfaceHash.clear();
            m_surfaceGridHash.take(surfaceId);

            m_surfaceGridModelIdsHash[surfaceId].clear();
            m_surfaceGridModelIdsHash.take(surfaceId);
        }, Qt::UniqueConnection);
        //@}

        return surfaceGrid;
    }

    void FITKSetSurfPolyProvider::deleteSetGridData(int setId)
    {
        // Get the set data.
        Interface::FITKModelSet* set = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKModelSet>(setId);
        if (!set)
        {
            return;
        }

        // Try to get the set data.
        if (!m_setGridHash.contains(setId))
        {
            return;
        }

        // Delete the grid data.
        QHash<int, vtkUnstructuredGrid*> subSetGridHash = m_setGridHash.take(setId);
        for (vtkUnstructuredGrid* ugrid : subSetGridHash)
        {
            if (ugrid)
            {
                ugrid->Delete();
            }
        }

        // Clear the model ids.
        m_setGridModelIdsHash.take(setId).clear();
    }

    void FITKSetSurfPolyProvider::deleteSurfaceGridData(int surfaceId)
    {
        // Get the surface data.
        Interface::FITKMeshSurface* surface = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKMeshSurface>(surfaceId);
        if (!surface)
        {
            return;
        }

        // Try to get the surface data.
        if (!m_surfaceGridHash.contains(surfaceId))
        {
            return;
        }

        // Delete the poly data.
        QHash<int, vtkUnstructuredGrid*> subSurfaceGridHash = m_surfaceGridHash.take(surfaceId);
        for (vtkUnstructuredGrid* grid : subSurfaceGridHash)
        {
            if (grid)
            {
                grid->Delete();
            }
        }

        // Clear the model ids.
        m_surfaceGridModelIdsHash.take(surfaceId).clear();
    }

    vtkUnstructuredGrid* FITKSetSurfPolyProvider::generateSetGrid(Interface::FITKModelSet* set, int& modelId, int instanceId)
    {
        if (!set)
        {
            return nullptr;
        }

        // Try to get the model data as an instance.
        Interface::FITKAbstractAssInstance* instanceParent = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKAbstractAssInstance>(set->getAbsModelID());
        if (instanceParent)
        {
            instanceId = set->getAbsModelID();
        }

        // Get set type and members.
        Interface::FITKModelEnum::FITKModelSetType type = set->getModelSetType();

        // Get set detail information.
        AbaqusData::AbaModelInfo setInfo = AbaqusData::FITKAbaModelInfo::GetModelInfo(set);

        // Get real members.
        const QList<int> & ids = set->getAbsoluteMember();

        // Judge if the model is part or assembly or instance.
        vtkUnstructuredGrid* setDataSet{ nullptr };

        bool isValid = false;

        // Part set. 
        // The instance id is -1 and the model set information has part data. 
        // The AbaModelInfo's part is the real parent data.
        //@{
        if (!isValid && (instanceId == -1) && setInfo._part)
        {
            modelId = setInfo._part->getDataObjectID();
            isValid = true;
            setDataSet = generateSetDataSetWithPart(setInfo._part, type, ids);
        }
        //@}

        // Instance set. 
        // The instance id is not -1 and the model set information has part data.
        // The AbaModelInfo's part is the original part data, and its real parent data is the instance with the instanceId.
        //@{
        if (!isValid && (instanceId != -1) && setInfo._part)
        {
            // Get the parent instance data.
            Interface::FITKAbstractAssInstance* instance = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKAbstractAssInstance>(instanceId);
            if (!instance)
            {
                return nullptr;
            }

            // Check the part data.
            AbaqusData::FITKAbaqusPart* part = instance->getModelT<AbaqusData::FITKAbaqusPart>();
            if (part == setInfo._part)
            {
                modelId = instance->getDataObjectID();
                isValid = true;
                setDataSet = generateSetDataSetWithInstance(instance, type, ids);
            }
        }
        //@}

        // Assembly instance set.
        // The instance id is not -1 and the model set information has instance data.
        //  The AbaModelInfo's instance or the data with 'instanceId' are the same, they are both the parent instance data.
        //@{
        if (!isValid && (instanceId != -1) && setInfo._instance)
        {
            modelId = setInfo._instance->getDataObjectID();
            isValid = true;
            setDataSet = generateSetDataSetWithInstance(setInfo._instance, type, ids);
        }
        //@}

        // Assembly set.
        // The instance id is -1 and the model set information has assembly data.
        // The AbaModelInfo's assembly is the parent data.
        //@{
        if (!isValid && (instanceId == -1) && setInfo._assembly)
        {
            modelId = setInfo._assembly->getDataObjectID();
            isValid = true;
            setDataSet = generateSetDataSetWithAssembly(setInfo._assembly, type, ids);
        }
        //@}

        // Check if this set is valid.
        if (!setDataSet || !isValid)
        {
            return nullptr;
        }

        return setDataSet;
    }

    vtkUnstructuredGrid* FITKSetSurfPolyProvider::generateSetDataSetWithPart(AbaqusData::FITKAbaqusPart* part, int type, const QList<int>& ids)
    {
        if (!part)
        {
            return nullptr;
        }

        // Get model mesh data.
        Interface::FITKAbaMeshPartVTK* meshData = part->getMesh();
        if (!meshData)
        {
            return nullptr;
        }

        // Get original mesh dataset.
        vtkUnstructuredGrid* meshGrid = meshData->getGrid();
        if (!meshGrid)
        {
            return nullptr;
        }

        // Initialize smart pointer filter.
        //@{
        vtkSmartPointer<vtkExtractSelection> extractor = vtkSmartPointer<vtkExtractSelection>::New();
        vtkSmartPointer<vtkSelectionNode> selectionNode = vtkSmartPointer<vtkSelectionNode>::New();
        vtkSmartPointer<vtkSelection> selection = vtkSmartPointer<vtkSelection>::New();
        vtkSmartPointer<vtkIdTypeArray> indiceArray = vtkSmartPointer<vtkIdTypeArray>::New();

        selectionNode->SetContentType(vtkSelectionNode::INDICES);
        selection->AddNode(selectionNode);
        extractor->SetInputData(1, selection);
        selectionNode->SetSelectionList(indiceArray);
        //@}

        // Set extraction type.
        switch (type)
        {
        case Interface::FITKModelEnum::FMSNode:
        {
            // Create points indice array.
            //@{   
            for (const int & id : ids)
            {
                int index = meshData->getNodeIndexByID(id);
                indiceArray->InsertNextValue(index);
            }
            //@}

            selectionNode->SetFieldType(vtkSelectionNode::POINT);
            break;
        }
        case Interface::FITKModelEnum::FMSElem:
        {
            // Create cells indice array.
            //@{   
            for (const int & id : ids)
            {
                int index = meshData->getEleIndexByID(id);
                indiceArray->InsertNextValue(index);
            }
            //@}

            selectionNode->SetFieldType(vtkSelectionNode::CELL);
            break;
        }
        // Not support other types for now.
        default:
        {
            return nullptr;
        }
        }

        extractor->SetInputData(meshGrid);
        extractor->Update();

        // Need to be deleted manually.
        vtkUnstructuredGrid* ugrid = vtkUnstructuredGrid::New();
        ugrid->DeepCopy(extractor->GetOutput());

        return ugrid;
    }

    vtkUnstructuredGrid* FITKSetSurfPolyProvider::generateSetDataSetWithInstance(Interface::FITKAbstractAssInstance* instance, int type, const QList<int>& ids)
    {
        // Get part.
        AbaqusData::FITKAbaqusPart* part = instance->getModelT<AbaqusData::FITKAbaqusPart>();

        // Gererate part set data first.
        vtkDataSet* dataset = generateSetDataSetWithPart(part, type, ids);

        // Get the transform of the instance.
        vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
        vtkSmartPointer<vtkTransformFilter> transformFilter = vtkSmartPointer<vtkTransformFilter>::New();

        transformFilter->SetInputData(dataset);
        transformFilter->SetTransform(transform);

        // Get the instance transform.
        FITKGraphCommons::GetInstanceTransform(instance, transform);

        // Update filter and delete the old data.
        transformFilter->Update();
        dataset->Delete();

        // Copy new data.
        vtkUnstructuredGrid* ugrid = vtkUnstructuredGrid::New();
        ugrid->DeepCopy(transformFilter->GetOutput());
        return ugrid;
    }

    vtkUnstructuredGrid* FITKSetSurfPolyProvider::generateSetDataSetWithAssembly(Interface::FITKAbaAssembly* assembly, int type, const QList<int>& ids)
    {
        // Not support cells for now.
        Q_UNUSED(type);

        if (!assembly)
        {
            return nullptr;
        }

        // Get assembly points.
        //@{
        Interface::FITKNodeList* nodes = assembly->getPoints();
        if (!nodes)
        {
            return nullptr;
        }
        //@}

        // Create mesh data.
        //@{
        vtkUnstructuredGrid* ugrid = vtkUnstructuredGrid::New();
        vtkPoints* pts = vtkPoints::New();
        ugrid->SetPoints(pts);

        // Get points and generate set mesh data with points.
        int nMember = 0;
        for (const int & id : ids)
        {
            // Generate set with ref-points.
            Core::FITKNode* node = nodes->getNodeByID(id);

            if (!node)
            {
                continue;
            }

            double pos[3]{ 0, 0, 0 };
            node->getCoor(pos);

            pts->InsertNextPoint(pos);

            vtkIdType cellPts[1]{ nMember };
            ugrid->InsertNextCell(VTKCellType::VTK_VERTEX, 1, cellPts);

            nMember++;
        }
        //@}

        return ugrid;
    }

    vtkUnstructuredGrid* FITKSetSurfPolyProvider::generateSurfaceGrid(Interface::FITKMeshSurface* surface, QList<int>& modelIds, int instanceId)
    {
        if (!surface)
        {
            return nullptr;
        }

        // Generate node surface.
        Interface::FITKMeshSurfaceNode* nodeSurf = dynamic_cast<Interface::FITKMeshSurfaceNode*>(surface);
        if (nodeSurf)
        {
            return generateNodeSurface(nodeSurf, modelIds, instanceId);
        }

        // Generate element surface.
        Interface::FITKMeshSurfaceElement* eleSurf = dynamic_cast<Interface::FITKMeshSurfaceElement*>(surface);
        if (eleSurf)
        {
            return generateElementSurface(eleSurf, modelIds, instanceId);
        }

        return nullptr;
    }

    vtkUnstructuredGrid* FITKSetSurfPolyProvider::generateElementSurface(Interface::FITKMeshSurfaceElement* eleSurf, QList<int>& modelIds, int instanceId)
    {
        if (!eleSurf)
        {
            return nullptr;
        }

        // Get all valid sets.
        QList<Interface::SurfaceAbsSetInfo> surfSetInfoList = eleSurf->getAbsoluteSetInfo();

        // Get the number of sets.
        int nSet = surfSetInfoList.count();

        QList<vtkUnstructuredGrid*> setDataSetList;

        for (int i = 0; i < nSet; i++)
        {
            Interface::SurfaceAbsSetInfo surfSetInfo = surfSetInfoList[i];

            // Get the set data.
            Interface::FITKModelSet* set = surfSetInfo._set;
            if (!set)
            {
                continue;
            }

            // Get the set's parent data.
            Interface::FITKAbstractModel* parent = surfSetInfo._model;

            // Get set's model information.
            AbaqusData::AbaModelInfo setInfo = AbaqusData::FITKAbaModelInfo::GetModelInfo(set->getDataObjectID(), instanceId);

            // Try to get the parent instance data and its transform and part data.
            vtkTransform* transform{ nullptr };
            AbaqusData::FITKAbaqusPart* part{ nullptr };
            Interface::FITKAbstractAssInstance* instance{ nullptr };

            bool isValid = false;

            // Try to get the model data as an instance.
            if (!isValid && setInfo._instance)
            {
                isValid = true;
                instance = setInfo._instance;
                part = setInfo._instance->getModelT<AbaqusData::FITKAbaqusPart>();
            }

            // Try to cast the parent data to instance.
            Interface::FITKAbstractAssInstance* instanceParent = dynamic_cast<Interface::FITKAbstractAssInstance*>(parent);
            if (!isValid && instanceParent)
            {
                isValid = true;
                instance = instanceParent;
                part = instanceParent->getModelT<AbaqusData::FITKAbaqusPart>();
            }

            // Try to cast the parent data to part.
            AbaqusData::FITKAbaqusPart* partParent = Core::FITKDataRepo::getInstance()->getTDataByID<AbaqusData::FITKAbaqusPart>(eleSurf->getAbsModelID());
            if (!isValid && partParent)
            {
                isValid = true;
                part = partParent;
            }

            // Try to check the instance.
            if (!isValid && setInfo._instance)
            {
                isValid = true;
                instance = setInfo._instance;
                part = setInfo._instance->getModelT<AbaqusData::FITKAbaqusPart>();
            }

            // Try to check to the part.
            if (!isValid && setInfo._part)
            {
                isValid = true;
                part = setInfo._part;
            }

            // Return.
            if (!part)
            {
                return nullptr;
            }

            // Get model mesh data.
            Interface::FITKAbaMeshPartVTK* meshData = part->getMesh();
            if (!meshData)
            {
                return nullptr;
            }

            int modelId = -1;

            // Get the transform.
            if (instance)
            {
                modelId = instance->getDataObjectID();

                transform = vtkTransform::New();
                FITKGraphCommons::GetInstanceTransform(instance, transform);
            }
            else
            {
                modelId = part->getDataObjectID();
            }

            if (!modelIds.contains(modelId))
            {
                modelIds.push_back(modelId);
            }

            // Get the set members.
            const QList<int> & ids = set->getAbsoluteMember();

            // Get the face type label.
            int label = surfSetInfo._surfaceIndex;
            // Interface::FITKAbaSurfaceLabel::AbaSurfLabel label = (Interface::FITKAbaSurfaceLabel::AbaSurfLabel)eleSurf->getSurfaceIndexAt(i);

            // Get all mesh data's points.
            vtkUnstructuredGrid* gridData = vtkUnstructuredGrid::New();
            vtkSmartPointer<vtkDoubleArray> normal = vtkSmartPointer<vtkDoubleArray>::New();
            normal->SetNumberOfComponents(3);
            gridData->Allocate();
            gridData->GetCellData()->SetNormals(normal);
            copyPoints(meshData, gridData);

            // Set extraction type.
            //@{
            // Create cells indice array.  
            for (const int & id : ids)
            {
                // Create the face cell.
                insertNextCell(meshData, gridData, id, label);
            }
            //@}   

            // Add the model ids array to point data and cell data.
            vtkSmartPointer<vtkIntArray> modelIdArrayPt = vtkSmartPointer<vtkIntArray>::New();
            modelIdArrayPt->SetName(c_modelIdArrayName);
            gridData->GetPointData()->AddArray(modelIdArrayPt);

            vtkSmartPointer<vtkIntArray> modelIdArrayCell = vtkSmartPointer<vtkIntArray>::New();
            modelIdArrayCell->SetName(c_modelIdArrayName);
            gridData->GetCellData()->AddArray(modelIdArrayCell);

            // Fill the model id array to point data.
            int nPt = gridData->GetNumberOfPoints();
            modelIdArrayPt->SetNumberOfComponents(1);
            modelIdArrayPt->SetNumberOfValues(nPt);
            modelIdArrayPt->FillComponent(0, modelId);

            // Fill the model id array to point data.
            int nCell = gridData->GetNumberOfCells();
            modelIdArrayCell->SetNumberOfComponents(1);
            modelIdArrayCell->SetNumberOfValues(nCell);
            modelIdArrayCell->FillComponent(0, modelId);

            // Get the transformed data.
            if (transform)
            {
                vtkUnstructuredGrid* transformedGrid = vtkUnstructuredGrid::New();
                FITKGraphCommons::GetTransformedDataSet(transform, gridData, transformedGrid);

                gridData->Delete();
                transform->Delete();

                setDataSetList.push_back(transformedGrid);
            }
            // Or get the data.
            else
            {
                setDataSetList.push_back(gridData);
            }
        }

        // Append data list.
        vtkAppendFilter* appFilter = vtkAppendFilter::New();
        for (vtkUnstructuredGrid* gridData : setDataSetList)
        {
            appFilter->AddInputData(gridData);
        }

        appFilter->Update();

        vtkUnstructuredGrid* surfaceGrid = vtkUnstructuredGrid::New();
        surfaceGrid->DeepCopy(appFilter->GetOutput());

        for (vtkUnstructuredGrid* gridData : setDataSetList)
        {
            gridData->Delete();
        }

        setDataSetList.clear();
        appFilter->Delete();

        return surfaceGrid;
    }

    vtkUnstructuredGrid* FITKSetSurfPolyProvider::generateNodeSurface(Interface::FITKMeshSurfaceNode* nodeSurf, QList<int>& modelIds, int instanceId)
    {
        if (!nodeSurf)
        {
            return nullptr;
        }

        // Get all valid sets.
        QList<Interface::SurfaceAbsSetInfo> surfSetInfoList = nodeSurf->getAbsoluteSetInfo();

        // Get the number of sets.
        int nSet = surfSetInfoList.count();

        QList<vtkUnstructuredGrid*> setDataSetList;

        for (int i = 0; i < nSet; i++)
        {
            Interface::SurfaceAbsSetInfo surfSetInfo = surfSetInfoList[i];

            // Get the set data.
            Interface::FITKModelSet* set = surfSetInfo._set;
            if (!set)
            {
                continue;
            }

            // Get the set's parent data.
            Interface::FITKAbstractModel* parent = surfSetInfo._model;

            // Get set's model information.
            AbaqusData::AbaModelInfo setInfo = AbaqusData::FITKAbaModelInfo::GetModelInfo(set->getDataObjectID(), instanceId);

            // Try to get the parent instance data and its transform and also part data.
            vtkTransform* transform{ nullptr };
            AbaqusData::FITKAbaqusPart* part{ nullptr };
            Interface::FITKAbstractAssInstance* instance{ nullptr };

            bool isValid = false;

            // Try to get the model data as an instance.
            if (setInfo._instance)
            {
                isValid = true;
                instance = setInfo._instance;
                part = setInfo._instance->getModelT<AbaqusData::FITKAbaqusPart>();
            }

            // Try to cast the parent data to instance.
            Interface::FITKAbstractAssInstance* instanceParent = dynamic_cast<Interface::FITKAbstractAssInstance*>(parent);
            if (!isValid && instanceParent)
            {
                isValid = true;
                instance = instanceParent;
                part = instanceParent->getModelT<AbaqusData::FITKAbaqusPart>();
            }

            // Try to cast the parent data to part.
            AbaqusData::FITKAbaqusPart* partParent = Core::FITKDataRepo::getInstance()->getTDataByID<AbaqusData::FITKAbaqusPart>(nodeSurf->getAbsModelID());
            if (!isValid && partParent)
            {
                isValid = true;
                part = partParent;
            }

            // Try to check the instance.
            if (!isValid && setInfo._instance)
            {
                isValid = true;
                instance = setInfo._instance;
                part = setInfo._instance->getModelT<AbaqusData::FITKAbaqusPart>();
            }

            // Try to check to the part.
            if (!isValid && setInfo._part)
            {
                isValid = true;
                part = setInfo._part;
            }

            // Delete the transform and return.
            if (!part)
            {
                return nullptr;
            }

            // Get model mesh data.
            Interface::FITKAbaMeshPartVTK* meshData = part->getMesh();
            if (!meshData)
            {
                return nullptr;
            }

            int modelId = -1;

            // Get the transform.
            if (instance)
            {
                modelId = instance->getDataObjectID();

                transform = vtkTransform::New();
                FITKGraphCommons::GetInstanceTransform(instance, transform);
            }
            else
            {
                modelId = part->getDataObjectID();
            }

            if (!modelIds.contains(modelId))
            {
                modelIds.push_back(modelId);
            }

            // Get the set members.
            const QList<int> & ids = set->getAbsoluteMember();

            // Get set's points.
            vtkUnstructuredGrid* gridData = vtkUnstructuredGrid::New();
            vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();         
            gridData->Allocate();
            gridData->SetPoints(points);

            // copyPoints(meshData, gridData);

            // Set extraction type.
            //@{
            // Copy points and create cells indice array.  
            for (const int & id : ids)
            {
                // Copy points.
                //@{
                Core::FITKNode* node = meshData->getNodeByID(id);
                double pos[3]{ 0, 0, 0 };

                // If the node is invlid, then add a (0, 0, 0) point instead,
                // it can let the cell which didn't use this point be showed correctly.
                if (node)
                {
                    node->getCoor(pos);
                }

                points->InsertNextPoint(pos);
                //@}

                // Create the vertex cell.
                insertNextPointCell(meshData, gridData, id);
            }
            //@}

            // Add the model ids array to point data and cell data.
            vtkSmartPointer<vtkIntArray> modelIdArrayPt = vtkSmartPointer<vtkIntArray>::New();
            modelIdArrayPt->SetName(c_modelIdArrayName);
            gridData->GetPointData()->AddArray(modelIdArrayPt);

            vtkSmartPointer<vtkIntArray> modelIdArrayCell = vtkSmartPointer<vtkIntArray>::New();
            modelIdArrayCell->SetName(c_modelIdArrayName);
            gridData->GetCellData()->AddArray(modelIdArrayCell);

            // Fill the model id array to point data.
            int nPt = gridData->GetNumberOfPoints();
            modelIdArrayPt->SetNumberOfComponents(1);
            modelIdArrayPt->SetNumberOfValues(nPt);
            modelIdArrayPt->FillComponent(0, modelId);

            // Fill the model id array to point data.
            int nCell = gridData->GetNumberOfCells();
            modelIdArrayCell->SetNumberOfComponents(1);
            modelIdArrayCell->SetNumberOfValues(nCell);
            modelIdArrayCell->FillComponent(0, modelId);

            // Get the transformed data.
            if (transform)
            {
                vtkUnstructuredGrid* transformedGrid = vtkUnstructuredGrid::New();
                FITKGraphCommons::GetTransformedDataSet(transform, gridData, transformedGrid);

                gridData->Delete();
                transform->Delete();

                setDataSetList.push_back(transformedGrid);
            }
            // Or get the data.
            else
            {
                setDataSetList.push_back(gridData);
            }
        }

        // Append data list.
        vtkAppendFilter* appFilter = vtkAppendFilter::New();
        for (vtkUnstructuredGrid* gridData : setDataSetList)
        {
            appFilter->AddInputData(gridData);
        }

        appFilter->Update();

        vtkUnstructuredGrid* surfaceGrid = vtkUnstructuredGrid::New();
        surfaceGrid->DeepCopy(appFilter->GetOutput());

        for (vtkUnstructuredGrid* gridData : setDataSetList)
        {
            gridData->Delete();
        }

        setDataSetList.clear();
        appFilter->Delete();

        return surfaceGrid;
    }

    void FITKSetSurfPolyProvider::copyPoints(Interface::FITKAbaMeshPartVTK* meshData, vtkUnstructuredGrid* gridData)
    {
        if (!meshData || !gridData)
        {
            return;
        }

        // Create points data.
        vtkPoints* points = gridData->GetPoints();
        if (!points)
        {
            points = vtkPoints::New();
            gridData->SetPoints(points);
        }

        // Copy nodes.
        int nNode = meshData->getNodeCount();
        for (int i = 0; i < nNode; i++)
        {
            Core::FITKNode* node = meshData->getNodeAt(i);
            double pos[3]{ 0, 0, 0 };

            // If the node is invlid, then add a (0, 0, 0) point instead,
            // it can let the cell which didn't use this point be showed correctly.
            if (node)
            {
                node->getCoor(pos);
            }

            points->InsertNextPoint(pos);
        }
    }

    void FITKSetSurfPolyProvider::insertNextCell(Interface::FITKAbaMeshPartVTK* meshData, vtkUnstructuredGrid* gridData, int cellId, int surfaceLabel)
    {
        // Get the face information.
        Interface::AbaEleSurface face = meshData->getEleFace(cellId, (Interface::FITKAbaSurfaceLabel::AbaSurfLabel)surfaceLabel);

        // For fast creating cells, this function does't has nullptr check,
        // make sure the input paremeters are ALL NOT NULLPTR.
        vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
        vtkSmartPointer<vtkPolygon> polygon = vtkSmartPointer<vtkPolygon>::New();   
        polygon->GetPointIds()->SetNumberOfIds(face._nodeIDs.count());

        double cellCenters[3]{ 0., 0., 0. };
        int idTemp = 0;
        for (int & id : face._nodeIDs)
        {
            int index = meshData->getNodeIndexByID(id);
            cell->InsertNextId(index);

            // For calculating polygon's normal and line's mid point.
            double pos[3]{ 0., 0., 0. };
            meshData->getNodeByID(id)->getCoor(pos);
            polygon->GetPoints()->InsertNextPoint(pos);
            polygon->GetPointIds()->SetId(idTemp, idTemp);
            idTemp++;

            for (int i = 0; i < 3; i++)
            {
                cellCenters[i] += pos[i];
            }
        }

        gridData->InsertNextCell(face._faceType, cell);

        // Get the face normal.
        double* pt{ nullptr };
        double normal[3]{ 0., 0., 0. };
        if (face._nodeIDs.count() == 2)
        {
            pt = polygon->GetPoints()->GetPoint(0);
            double pt1[3]{ pt[0], pt[1], pt[2] };
            pt = polygon->GetPoints()->GetPoint(1);
            double pt2[3]{ pt[0], pt[1], pt[2] };

            double edgeDirec[3]{ 0., 0., 0. };

            for (int i = 0; i < 3; i++)
            {
                cellCenters[i] /= 2;

                edgeDirec[i] = pt2[i] - pt1[i];
            }

            double edgeNormal[3]{ 0., 0., 0. };
            double scaleEdgeNor = sqrt(pow(edgeDirec[0], 2) + pow(edgeDirec[1], 2) + pow(edgeDirec[2], 2));
            if (!FITKGraphCommons::FuzzyCompare(scaleEdgeNor, 0.))
            {
                for (int i = 0; i < 3; i++)
                {
                    edgeNormal[i] = edgeDirec[i] / scaleEdgeNor;
                }
            }

            // Get the full element and get the cell center.( If the face is a line, the cell must be a poly cell,
            // so the cell center will be the center point of the line's face.)
            Interface::FITKAbstractElement* element = meshData->getElementByID(cellId);
            int nNode = element->getNodeCount();

            double fullCellCenter[3]{ 0., 0., 0. };
            double ptMin[3]{ 0., 0., 0. };
            double ptMax[3]{ 0., 0., 0. };
            vtkSmartPointer<vtkPolygon> polygonFullCell = vtkSmartPointer<vtkPolygon>::New();
            polygonFullCell->GetPointIds()->SetNumberOfIds(nNode);

            for (int i = 0; i < nNode; i++)
            {
                int nodeId = element->getNodeID(i);
                double nodePos[3]{ 0., 0., 0. };
                meshData->getNodeByID(nodeId)->getCoor(nodePos);

                for (int j = 0; j < 3; j++)
                {
                    fullCellCenter[j] += nodePos[j];
                }

                polygonFullCell->GetPoints()->InsertNextPoint(nodePos);
                polygonFullCell->GetPointIds()->SetId(i, i);

                if (i == 0)
                {
                    for (int j = 0; j < 3; j++)
                    {
                        ptMin[j] = nodePos[j];
                        ptMax[j] = nodePos[j];
                    }
                }
                else
                {
                    for (int j = 0; j < 3; j++)
                    {
                        ptMin[j] = qMin(ptMin[j], nodePos[j]);
                        ptMax[j] = qMax(ptMax[j], nodePos[j]);
                    }
                }
            }

            for (int i = 0; i < 3; i++)
            {
                fullCellCenter[i] /= nNode;
            }

            double fullCellNormal[3]{ 0., 0., 0. };
            vtkPolygon::ComputeNormal(polygonFullCell->GetPoints(), fullCellNormal);

            // The full cell plane.
            double crossPoint1[3]{ 0., 0., 0. };
            double crossPoint2[3]{ 0., 0., 0. };

            int flag = FITKGraphCommons::IntersectPlaneWithFinitePlane(edgeNormal, cellCenters, fullCellCenter, ptMin, ptMax, crossPoint1, crossPoint2);

            // IntersectWithFinitePlane
            // int flag = plane->IntersectWithLine(pt1, pt2, t, crossPoint);
            if (flag)
            {
                // Get the cross point.( One of the cross point may be the line's mid point. ) 
                bool hasValidCrossPoint = false;
                double crossPoint[3]{ 0., 0., 0. };

                if (!hasValidCrossPoint)
                {
                    if (!FITKGraphCommons::FuzzyCompare(cellCenters[0], crossPoint1[0]) 
                        || !FITKGraphCommons::FuzzyCompare(cellCenters[1], crossPoint1[1]) 
                        || !FITKGraphCommons::FuzzyCompare(cellCenters[2], crossPoint1[0]))
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            crossPoint[i] = crossPoint1[i];
                        }

                        hasValidCrossPoint = true;
                    }
                }

                if (!hasValidCrossPoint)
                {
                    if (!FITKGraphCommons::FuzzyCompare(cellCenters[0], crossPoint2[0]) 
                        || !FITKGraphCommons::FuzzyCompare(cellCenters[1], crossPoint2[1])
                        || !FITKGraphCommons::FuzzyCompare(cellCenters[2], crossPoint2[0]))
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            crossPoint[i] = crossPoint2[i];
                        }

                        hasValidCrossPoint = true;
                    }
                }

                double normalDirect[3]{ 0., 0., 0. };

                // The cross point to the mid point of the line is the normal direction.
                for (int i = 0; i < 3; i++)
                {
                    normalDirect[i] = cellCenters[i] - crossPoint[i];
                }

                double scale = sqrt(pow(normalDirect[0], 2) + pow(normalDirect[1], 2) + pow(normalDirect[2], 2));
                if (!FITKGraphCommons::FuzzyCompare(scale, 0.))
                {
                    for (int i = 0; i < 3; i++)
                    {
                        normal[i] = normalDirect[i] / scale;
                    }
                }
            }
        }
        else
        {
            vtkPolygon::ComputeNormal(polygon->GetPoints(), normal);

            // Deal with the negative face normal.
            if (surfaceLabel == Interface::FITKAbaSurfaceLabel::AbaSurfLabel::SPOS)
            {
                for (int i = 0; i < 3; i++)
                {
                    normal[i] *= -1;
                }
            }
        }

        // Add the normal to the data array.
        gridData->GetCellData()->GetNormals()->InsertNextTuple3(normal[0], normal[1], normal[2]);
    }

    void FITKSetSurfPolyProvider::insertNextPointCell(Interface::FITKAbaMeshPartVTK* meshData, vtkUnstructuredGrid* gridData, int id)
    {
        // For fast creating cells, this function does't has nullptr check,
        // make sure the input paremeters are ALL NOT NULLPTR, and the id 
        // of the node IS VALID.
        Core::FITKNode* node = meshData->getNodeByID(id);
        double pos[3]{ 0,0,0 };
        node->getCoor(pos);
        int index = gridData->GetPoints()->InsertNextPoint(pos);

        vtkIdType cellPts[1]{ index };
        gridData->InsertNextCell(VTKCellType::VTK_VERTEX, 1, cellPts);
    }
}   // namespace Exchange